package org.agora.games.bodybuilder;

import org.agora.games.bodybuilder.geometry.FixtureDefinition;
import org.agora.games.bodybuilder.geometry.Line;
import org.agora.games.bodybuilder.ui.MainForm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import svg.parser.Vertex;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @author : Arash Bizhanzadeh <arash.bizhanzadeh@cbc.ca>
 *         Date: 12-01-02
 *         Time: 6:11 PM
 *         CBC.ca All rights reserved.
 */
public class ImagePanel extends JPanel implements Scrollable, MouseListener, MouseMotionListener {

    private BufferedImage bufferedImage;
    private static Logger logger = LoggerFactory.getLogger(ImagePanel.class);
    private List<FixtureDefinition> fixtures;
    private float zoom;
    private Vertex selectedVertex;
    private Vertex draggableVertex;
    private List<Line> lines;
    private JPopupMenu linePopup;
    private Line selectedLine;
    private int selectedTriangle;
    private JPopupMenu polygonPopup;
    private FixtureDefinition selectedFixture;
    private static final Color SELECTED_VERTEX_COLOR = new Color(112, 112, 112, 100);
    private  final Color SELECTED_LINE_COLOR = new Color(155,0,0,155);
    private final Color DEFAULT_COLOR =   new Color(0,0,255,150);
    private final static Random random = new Random();

    public ImagePanel() {
        super();
        fixtures = new ArrayList<FixtureDefinition>();
        zoom = 1.0f;
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
        selectedVertex = null;
        lines = new ArrayList<Line>();
        linePopup = new JPopupMenu();

        JMenuItem delete = new JMenuItem("Delete line");
        linePopup.add(delete);
        delete.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                deleteLine();
            }
        });
        polygonPopup = new JPopupMenu();

        JMenuItem addParameters = new JMenuItem("Add Parameters");
        addParameters.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                 JDialog f = new FixtureParameters(MainForm.instance,true);
                f.pack();
                f.setLocationRelativeTo(MainForm.instance);
                f.setLocation(MainForm.instance.getWidth()/2, MainForm.instance.getHeight()/2);
                f.setVisible(true);
            }
        });
        polygonPopup.add(addParameters);
        selectedTriangle = -1;
    }

    private void deleteLine() {
        if(selectedVertex  != null){
           selectedFixture.getPerimeter().remove(selectedVertex);
        }
        this.invalidate();
        this.getParent().repaint();
    }


    public void setImage(File file) {
        try {
            bufferedImage = ImageIO.read(file);
            fixtures = new ArrayList<FixtureDefinition>();
            setAutoscrolls(true);
        } catch (IOException e) {
            logger.error("${MESSAGE}", e);
        }
    }

    @Override
    protected void paintComponent(Graphics graphics) {
        if(bufferedImage == null) { return ; }
        this.setSize(new Dimension(Math.round(bufferedImage.getWidth() * zoom), Math.round(bufferedImage.getHeight() * zoom)));
        this.setPreferredSize(this.getSize());
        Graphics2D g = (Graphics2D) graphics;
        g.scale(zoom, zoom);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.clearRect(0, 0, this.getWidth(), this.getHeight());
        graphics.drawImage(bufferedImage, 0, 0, null);

        g.setColor(DEFAULT_COLOR);
      
       paintFixtures(g);
    }


    private void paintFixtures(Graphics g) {
        
        for( FixtureDefinition f : fixtures){
            Color fixtureColor = f.getColor();
            g.setColor(fixtureColor);
            g.fillOval((int)f.getCenter().x, (int) f.getCenter().y, 2,2);
            Vertex oldVertex = null;
            for( Vertex v : f.getPerimeter()){
               //draw vertex
                if(selectedVertex!=null &&  v == selectedVertex){
                    g.setColor(SELECTED_VERTEX_COLOR);
                }
                    g.fillOval((int)v.x, (int) v.y, 2,2);
                if(selectedVertex!= null && v == selectedVertex){
                    g.setColor(fixtureColor);
                }
                //draw radius lines
                if(selectedLine!= null && v == selectedLine.end) {
                    g.setColor(SELECTED_LINE_COLOR);
                    g.drawLine(Math.round(f.getCenter().x),Math.round(f.getCenter().y)
                                    ,Math.round(v.x),Math.round(v.y));
                    g.setColor(fixtureColor);
                } else {
                    g.drawLine(Math.round(f.getCenter().x),Math.round(f.getCenter().y)
                                    ,Math.round(v.x),Math.round(v.y));
                }
                //draw perimeter
                if(oldVertex!=null){
                    g.drawLine(Math.round(oldVertex.x),Math.round(oldVertex.y)
                                                        ,Math.round(v.x),Math.round(v.y));
                }
                oldVertex = v;
            }
            if(oldVertex != null){
            g.drawLine(Math.round(oldVertex.x),Math.round(oldVertex.y)
                                                ,Math.round(f.getPerimeter().get(0).x),Math.round(f.getPerimeter().get(0).y));
            }
        }
    }

    public void setZoom(float v) {
        this.zoom = v;
        this.revalidate();
        this.getParent().repaint();
    }

    @Override
    public Dimension getPreferredScrollableViewportSize() {
        return getPreferredSize();
    }

    @Override
    public int getScrollableUnitIncrement(Rectangle rectangle, int i, int i1) {
        return 1;
    }

    @Override
    public int getScrollableBlockIncrement(Rectangle rectangle, int i, int i1) {
        return 1;
    }

    @Override
    public boolean getScrollableTracksViewportWidth() {
        return false;
    }

    @Override
    public boolean getScrollableTracksViewportHeight() {
        return false;
    }


    @Override
    public void mouseClicked(MouseEvent mouseEvent) {
        selectedFixture = null;
        selectedVertex = null;
        selectedLine = null;
        Vertex clickedPoint = new Vertex(mouseEvent.getX()/zoom, mouseEvent.getY()/zoom);
        if(mouseEvent.getButton() == MouseEvent.BUTTON3){
            logger.debug("Right Clicked");
            for(FixtureDefinition f : fixtures){
                
                if( (selectedVertex =f.isOnRadiusLine(clickedPoint)) != null ){
                    selectedLine = new Line(f.getCenter(),selectedVertex);
                    selectedFixture = f;
                    this.repaint();
                    linePopup.show(mouseEvent.getComponent(),mouseEvent.getX(),mouseEvent.getY());
                    return;
                }
            }
            for(FixtureDefinition f : fixtures){
                if(f.contains(clickedPoint)){
                    selectedFixture = f;
                    polygonPopup.show(mouseEvent.getComponent(),mouseEvent.getX(),mouseEvent.getY());
                    break;
                }
            }
            //if right clicked inside polygon

        } else {
        logger.debug("mouse clicked at {},{}", mouseEvent.getX(), mouseEvent.getY());
        for( FixtureDefinition f : fixtures){
            if(
                    (selectedVertex = f.hasVertexLike(clickedPoint)) != null
                    ){
                return;
            }
        }
//        selectedTriangle = selectTriangleFor(clickedPoint);

        this.revalidate();
        this.getParent().repaint();
        }
    }


//    private int selectTriangleFor(Vertex clickedPoint) {
//       for(Vertex center : triangles.keySet()){
//            List<Vertex> points = triangles.get(center);
//            for(int i=0; i <points.size() -1; i++){
//                Vertex point1 = points.get(i);
//                Vertex point2 = points.get(i+1);
//                Vertex v1 = point1.sub(center);
//                Vertex v2 = point2.sub(center);
//                float a = ( det(clickedPoint,v2) - det(center,v2))/ det(v1,v2);
//                float b = ( det(clickedPoint,v1) - det(center,v1))/ det(v1,v2) * -1 ;
//                if( a >0 && b >0 && a+b <1){
//                    logger.debug("Triangle selected {}" ,i);
//                    return i;
//                }
//
//            }
//           //check the first triangle
//                           Vertex point1 = points.get(points.size()-1);
//                           Vertex point2 = points.get(0);
//                           Vertex v1 = point1.sub(center);
//                           Vertex v2 = point2.sub(center);
//                           float a = ( det(clickedPoint,v2) - det(center,v2))/ det(v1,v2);
//                           float b = ( det(clickedPoint,v1) - det(center,v1))/ det(v1,v2) * -1 ;
//                           if( a >0 && b >0 && a+b <1){
//                                return points.size()-1;
//                           }
//
//        }
//        return -1;
//    }

    private float det(Vertex v1, Vertex v2) {
        return v1.x * v2.y - v1.y * v2.x;
    }

    @Override
    public void mousePressed(MouseEvent mouseEvent) {
        Vertex clicked = new Vertex(Math.round(mouseEvent.getX() / zoom), Math.round(mouseEvent.getY() / zoom));
        draggableVertex = null;
        for(FixtureDefinition f : fixtures){
            if((draggableVertex = f.hasVertexLike(clicked))!=null){
                return;
            }
        }
    }

    @Override
    public void mouseReleased(MouseEvent mouseEvent) {
        draggableVertex = null;
    }

    @Override
    public void mouseEntered(MouseEvent mouseEvent) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void mouseExited(MouseEvent mouseEvent) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void mouseDragged(MouseEvent mouseEvent) {
        if(draggableVertex!= null){
            draggableVertex.x = mouseEvent.getX()/zoom;
            draggableVertex.y = mouseEvent.getY()/zoom;
            this.revalidate();
            this.getParent().repaint();
        }
    }

    @Override
    public void mouseMoved(MouseEvent mouseEvent) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void setFixtures(List<FixtureDefinition> fixtures) {
        this.fixtures = fixtures;
    }

    public List<FixtureDefinition> getFixtures() {
        return fixtures;
    }
}
